/* Parallel Elliptic Curve Cryptography Engine - An OpenSSL Engine which
 * uses the SPU Elliptic Curve Library to provide optimized functionality
 * on the CBEA.
 *
 * Copyright (C) 2008  Michael Annichiarico (mike.gcdeveloper@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "pecc.h"

#include "./spu/pecc-stage4_spuppu.h"

#include <stdlib.h>
#include <stdio.h>
#include <libspe2.h>
#include <sys/wait.h>
#include <string.h>

#include "ecs_locl.h"
#include <openssl/err.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>


#define DEBUG_do(x)
//#define DEBUG_do(x) {if(1) {x}}

#define BN2spuarg(lst,bn) \
	lst.list[_i].data.all64=bn->d; lst.list[_i++].size=32+0*sizeof(bn->d[0])*bn->top; \
	/* printf("BN2spuarg: " #bn "->top == %d d= ",bn->top); \
	for(_j=0;_j<bn->top;_j++) printf(" %016lx",bn->d[_j]); \
	printf("\n"); */

extern spe_program_handle_t pecc_stage4_spu;
spe_context_ptr_t pecc_stage4_spu_speid;


int myspe_init(spe_context_ptr_t* speid, spe_program_handle_t* handle) {
	int rc;
  /* Create context */
  if ((*speid = spe_context_create (0, NULL)) == NULL)
    {
      fprintf (stderr, "Failed spe_context_create(errno=%d strerror=%s)\n", errno, strerror(errno));
	return 0;
    }
  /* Load program */
  if ((rc = spe_program_load (*speid, handle)) != 0)
    {
      fprintf (stderr, "Failed spe_program_load(errno=%d strerror=%s)\n", errno, strerror(errno));
	return 0;
    }
	return 1;
}


int myspe_fini(spe_context_ptr_t speid) {
	int rc;
  /* Destroy context */
  if ((rc = spe_context_destroy (speid)) != 0)
    {
      fprintf (stderr, "Failed spe_context_destroy(rc=%d, errno=%d strerror=%s)\n", rc, errno, strerror(errno));
	return 0;
    }else return 1;
/*
  if (stopinfo.stop_reason == SPE_EXIT) {
	fprintf(stderr, "SPE_EXIT -- OK! code %d\n",stopinfo.result.spe_exit_code);
	return 1
  }
  else {
	fprintf(stderr, "stopinfo.stop_reason=%x, stopinfo.spe_exit_code=%x \n", 
		stopinfo.stop_reason,
		stopinfo.result.spe_exit_code);
        return 0;
  }
*/
}

int myspe_threadrun_function(void* pargs)  {/* Thread function */
	void** args = (void**)pargs;
	*(int*)args[3] = myspe_nothreadrun(args[0],args[1],args[2]);
	pthread_exit(NULL);
}
int myspe_threadrun(spe_context_ptr_t speid,spe_stop_info_t* stopinfo,void* argp) {
	pthread_t my_thread;
	int retval,spe_retval=0;
	void* args[4];
	args[0] = speid;
	args[1] = stopinfo;
	args[2] = argp;
	args[3] = &spe_retval;

	/* Create Thread */
	retval = pthread_create(
		&my_thread, /* Thread object */
		NULL, /* Thread attributes */
		myspe_threadrun_function, /* Thread function */
		args /* Thread argument */
	);

	/* Check for thread creation errors */
	if(retval) {
		fprintf(stderr, "Error creating thread! Exit code is: %d\n", retval);
		return(-1);
	}

	/* Wait for Thread Completion */
	retval = pthread_join(my_thread, NULL);
	/* Check for thread joining errors */
	if(retval) {
		fprintf(stderr, "Error joining thread! Exit code is: %d\n", retval);
		return(-1);
	}

	return spe_retval;
}


int myspe_nothreadrun(spe_context_ptr_t speid,spe_stop_info_t* stopinfo,void* argp) {
	int rc;
  unsigned int entry = SPE_DEFAULT_ENTRY;

//  if(myspe_init(speid,&pecc_stage4_spu)!=1) return -2;

  /* Run context */
  if ((rc = spe_context_run(speid, &entry, 0, argp, NULL, stopinfo)) != 0)
    {
      fprintf (stderr, "Failed spe_context_run(errno=%d strerror=%s,speid=%p)\n", errno, strerror(errno),speid);
	return -4;
    }

  
//  if(myspe_fini(speid)!=1) return -3;

	return 0;

}

int myspe_run(spe_context_ptr_t* speid,spe_stop_info_t* stopinfo,void* argp) {
	if(speid==NULL) return -3;
	if(*speid==NULL) {
		if(myspe_init(speid,&pecc_stage4_spu)!=1) return -2;
	}
	return(myspe_threadrun(*speid,stopinfo,argp));
}

int pecc_init(ENGINE* e) {
	pecc_stage4_spu_speid=NULL;
//	return(myspe_init(&pecc_stage4_spu_speid,&pecc_stage4_spu));
	return 1;
}

int pecc_fini(ENGINE* e) {
//	return(myspe_fini(&pecc_stage4_spu_speid));
	if(pecc_stage4_spu_speid!=NULL) {
		myspe_fini(pecc_stage4_spu_speid);
		pecc_stage4_spu_speid=NULL;
	}
	return 1;
}

int speRUN() {
	int i=0;
	vui r;
	union {vui s; unsigned int a[4];} s;
	vui a = {2,2,2,2};
	vui b = {1,2,3,0xffffffff};
	vui m = {3,4,5,0};
	spu_add_t addarg;
	spu_cmd_t cmd;
	spu_elem_t elem;
	memset(&s,0,sizeof(s));
	addarg.s=s.s;
	addarg.a=a;
	addarg.b=b;
	addarg.m=m;
	addarg.size=1;
	elem.data.all64=(unsigned long long)&addarg;
	elem.size=sizeof(addarg);
	cmd.type=0; //ADD
	cmd.arg=elem;
	printf("from PPU: &cmd == %llx\n",(unsigned long long) &cmd);
	i = myspe_run(&pecc_stage4_spu_speid,NULL,(void*)&cmd);
	s.s = addarg.s;
	printf("from PPU: s == %08x%08x%08x%08x\n",s.a[0],s.a[1],s.a[2],s.a[3]);
	
	printf("from PPU: run ecdsa_sign\n");
	if(1){
		//spu_mod_mult_t mmarg;
		//mmarg.
		int spu_ecdsa_result __attribute__((aligned(16))) = -10;
		int _i,_j;
//#define mike_endianswap(a,b,c,d,e,f,g,h) e,f,g,h,a,b,c,d
#define mike_endianswap(a,b,c,d,e,f,g,h) e,f,g,h,a,b,c,d
		PECC_BN_t d={.a={mike_endianswap(0,0,0,0,0,0,0,2)}};
		PECC_BN_t e={.a={mike_endianswap(1,1,1,1,0,0,0,0)}};
		PECC_BN_t k={.a={mike_endianswap(0,0,0,0,0,0,4,4)}};
		PECC_BN_t Qx = { .a={mike_endianswap(0x00000084,0x5fd61638,0xbac7d9e1,0x09a67a1f,0x7047dc0f,0xd9a5488a,0x8468364b,0xdc592aad)}};
		PECC_BN_t Qy = { .a={mike_endianswap(0x0000001b,0x1420774a,0xbba2587c,0x83900984,0x765a8a85,0xd776325f,0xc39cc782,0x3d734660)}};
		PECC_BN_t Qz = { .a={mike_endianswap(0,0,0,0,0,0,0,1)}};

		PECC_BN_t r,s;
		spu_list_t arg_list;
		arg_list.size=4;
		arg_list.list[0].data.all64=&d;
		arg_list.list[0].size=sizeof(d);
		arg_list.list[1].data.all64=&e;
		arg_list.list[1].size=sizeof(e);
		arg_list.list[2].data.all64=&k;
		arg_list.list[2].size=sizeof(k);
		arg_list.list[3].data.all64=&r;
		arg_list.list[3].size=sizeof(r);
		arg_list.list[4].data.all64=&s;
		arg_list.list[4].size=sizeof(s);
		arg_list.list[5].data.all64=&spu_ecdsa_result;
		arg_list.list[5].size=sizeof(spu_ecdsa_result);
		cmd.type=2; //ecdsa_sign
		elem.size=sizeof(arg_list);
		elem.data.all64=&arg_list;
		cmd.arg=elem;
		i = myspe_run(&pecc_stage4_spu_speid,NULL,(void*)&cmd);
		printf("result: %d\n",spu_ecdsa_result);
		printf("from PPU: r: %08x %08x %08x %08x\n",r.a[0],r.a[1],r.a[2],r.a[3]);
		printf("from PPU: r2: %08x %08x %08x %08x\n",r.a[4],r.a[5],r.a[6],r.a[7]);
		printf("from PPU: s: %08x %08x %08x %08x\n",s.a[0],s.a[1],s.a[2],s.a[3]);
		printf("from PPU: s2: %08x %08x %08x %08x\n",s.a[4],s.a[5],s.a[6],s.a[7]);
		printf("PPU: calling spu ecdsa_verify\n");
		cmd.type=3; //ecdsa_verify
		_i=0;
#define	 Val2spuarg(lst,val) lst.list[_i].data.all64=&val; lst.list[_i++].size=sizeof(val); \
	for(_j=0;_j<2;_j++) printf(" %016lx",val.v[_j]); \
	printf("\n");
		Val2spuarg(arg_list,Qx);
		Val2spuarg(arg_list,Qy);
//		Val2spuarg(arg_list,Qz);
		Val2spuarg(arg_list,e);
		Val2spuarg(arg_list,r);
		Val2spuarg(arg_list,s);
		arg_list.list[_i].data.all64=&spu_ecdsa_result;
		arg_list.list[_i++].size=sizeof(spu_ecdsa_result);
		arg_list.size=_i-1;
		i = myspe_run(&pecc_stage4_spu_speid,NULL,(void*)&cmd);
		printf("ecdsa_verify result: %d\n",spu_ecdsa_result);
	}

	return(i);
}

int speTEST() {
  spe_context_ptr_t speid;
  int rc;
  spe_stop_info_t stopinfo;
  unsigned int entry = SPE_DEFAULT_ENTRY;

  /* Create context */
  if ((speid = spe_context_create (0, NULL)) == NULL)
    {
      fprintf (stderr, "Failed spe_context_create(errno=%d strerror=%s)\n", errno, strerror(errno));
	return -2;
//      exit (1);
    }
  /* Load program */
  if ((rc = spe_program_load (speid, &pecc_stage4_spu)) != 0)
    {
      fprintf (stderr, "Failed spe_program_load(errno=%d strerror=%s)\n", errno, strerror(errno));
	return -3;
//      exit (1);
    }
  /* Run context */
  if ((rc = spe_context_run(speid, &entry, 0, NULL, NULL, &stopinfo)) != 0)
    {
      fprintf (stderr, "Failed spe_context_run(errno=%d strerror=%s)\n", errno, strerror(errno));
	return -4;
//      exit (1);
    }
  /* Destroy context */
  if ((rc = spe_context_destroy (speid)) != 0)
    {
      fprintf (stderr, "Failed spe_context_destroy(rc=%d, errno=%d strerror=%s)\n", rc, errno, strerror(errno));
	return -5;
//      exit (1);
    }

  if (stopinfo.stop_reason == SPE_EXIT) {
	fprintf(stderr, "SPE_EXIT -- OK!\n");
        return (stopinfo.result.spe_exit_code);
  }
  else {
	fprintf(stderr, "stopinfo.stop_reason=%x, stopinfo.spe_exit_code=%x \n", 
		stopinfo.stop_reason,
		stopinfo.result.spe_exit_code);
        return -1;
  }
}

extern ECDSA_SIG * (*old_ecdsa_do_sign)(const unsigned char *dgst, int dgst_len, 
			const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
static ECDSA_SIG * pecc_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, 
			const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
extern int (*old_ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, 
			const ECDSA_SIG *sig, EC_KEY *eckey);
static int pecc_ecdsa_do_verify(const unsigned char *dgst, int dgst_len, 
			const ECDSA_SIG *sig, EC_KEY *eckey);

#include<unistd.h>
ECDSA_SIG * pecc_sign(const unsigned char *dgst, int dgst_len, 
			const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) {
#if 0
	fprintf(stderr,"TESTING EC_KEY values!\n");
	{
		BIGNUM *priv_key;
		const EC_GROUP* group;
		EC_POINT* pub_key = EC_KEY_get0_public_key(eckey);
		group    = EC_KEY_get0_group(eckey);
		const EC_POINT* gen = EC_GROUP_get0_generator(group);
		priv_key = EC_KEY_get0_private_key(eckey);
		//priv_key = BN_hex2bn("02");
//		pub_key = EC_GROUP

		fprintf(stderr,"pri_key:\n");
		fprintf(stderr,"::  = %s\n",BN_bn2hex(priv_key));
		{
		BN_CTX* ctx = BN_CTX_new();
		int _j,_i=0;
		spu_cmd_t cmd;
		spu_elem_t elem;
		spu_list_t arg_list;
		BIGNUM* Qx     = BN_CTX_get(ctx);
		BIGNUM* Qy     = BN_CTX_get(ctx);
		BIGNUM* Qz     = BN_value_one();
		EC_POINT_get_affine_coordinates_GF2m(group,pub_key,Qx,Qy,ctx);
		fprintf(stderr,"pub_key:\n");
		fprintf(stderr,"::X = %s\n",BN_bn2hex(Qx));
		fprintf(stderr,"::Y = %s\n",BN_bn2hex(Qy));
		EC_POINT_get_affine_coordinates_GF2m(group,gen,Qx,Qy,ctx);
		fprintf(stderr,"generator:\n");
		fprintf(stderr,"::X = %s\n",BN_bn2hex(Qx));
		fprintf(stderr,"::Y = %s\n",BN_bn2hex(Qy));
		elem.size=sizeof(arg_list);
		elem.data.all64=&arg_list;
		cmd.arg=elem;
		printf("PPU: calling spu scalar_mult\n");
		cmd.type=4; //scalar_mult
		arg_list.size=4;
		BN2spuarg(arg_list,priv_key);
		BN2spuarg(arg_list,Qx);
		BN2spuarg(arg_list,Qy);
		BN2spuarg(arg_list,Qz);
		_i = myspe_run(&pecc_stage4_spu_speid,NULL,(void*)&cmd);
		BN_CTX_free(ctx);
		}
	}
#endif
//	fprintf(stderr,"PECC-S4_TEST SIGNING!\n");
	return(pecc_ecdsa_do_sign(dgst,dgst_len,inv,rp,eckey));
}
int pecc_verify(const unsigned char *dgst, int dgst_len, 
			const ECDSA_SIG *sig, EC_KEY *eckey) {
	int i;
//	i=speRUN();
//	fprintf(stderr,"PECC-S4_TEST VERIFYING!\n");
	return(pecc_ecdsa_do_verify(dgst,dgst_len, 
			sig, eckey));
}


int pecc_EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
	const EC_POINT *point, const BIGNUM *p_scalar, BN_CTX *ctx) {
	
	return(EC_POINT_mul(group, r, g_scalar, point, p_scalar, ctx));
}

int pecc_EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP *group, const EC_POINT *point,
	BIGNUM *x, BIGNUM *y, BN_CTX *ctx) {

	return(EC_POINT_get_affine_coordinates_GF2m(group, point, x, y, ctx));
}

static int pecc_ecdsa_do_verify(const unsigned char *dgst, int dgst_len,
		const ECDSA_SIG *sig, EC_KEY *eckey)
{
	int ret = -1;
	BN_CTX   *ctx;
	BIGNUM   *order, *u1, *u2, *m, *X, *Qx,*Qy,*Qz;
	EC_POINT *point = NULL;
	const EC_GROUP *group;
	const EC_POINT *pub_key;

	/* check input values */
	if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL ||
	    (pub_key = EC_KEY_get0_public_key(eckey)) == NULL || sig == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_MISSING_PARAMETERS);
		return -1;
	}

	ctx = BN_CTX_new();
	if (!ctx)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_MALLOC_FAILURE);
		return -1;
	}
	BN_CTX_start(ctx);
	order = BN_CTX_get(ctx);	
	m     = BN_CTX_get(ctx);
/*
	u1    = BN_CTX_get(ctx);
	u2    = BN_CTX_get(ctx);
	m     = BN_CTX_get(ctx);
	X     = BN_CTX_get(ctx);
	if (!X)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
*/
	
	if (!EC_GROUP_get_order(group, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
		goto err;
	}

	DEBUG_do({
		printf("&order.d : %p\n",order->d);
		printf("&order.dmax : %d\n",order->dmax);
	})
#if 1 
	if (BN_is_zero(sig->r)          || BN_is_negative(sig->r) || 
	    BN_ucmp(sig->r, order) >= 0 || BN_is_zero(sig->s)  ||
	    BN_is_negative(sig->s)      || BN_ucmp(sig->s, order) >= 0)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_BAD_SIGNATURE);
		ret = 0;	/* signature is invalid */
		goto err;
	}
#endif
	/* calculate tmp1 = inv(S) mod order */
#if 0
	if (!BN_mod_inverse(u2, sig->s, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}

	DEBUG_do({
		printf("&u2.d (as inverse) : %p\n",u2->d);
	})
#endif
	/* digest -> m */
	if (!BN_bin2bn(dgst, dgst_len, m))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}

	DEBUG_do({ printf("converted digest to \' m \'\n"); })

#if 0
	/* u1 = m * tmp mod order */
	if (!BN_mod_mul(u1, m, u2, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}

	DEBUG_do({
		printf("&u1.d : %p\n",u1->d);
		printf("&u1.dmax : %d\n",u1->dmax);
	})

	/* u2 = r * w mod q */
	if (!BN_mod_mul(u2, sig->r, u2, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}

	DEBUG_do({
		printf("&u2.d : %p\n",u2->d);
		printf("&u2.dmax : %d\n",u2->dmax);
	})


	if ((point = EC_POINT_new(group)) == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_MALLOC_FAILURE);
		goto err;
	}
	/* SPEEDUP? */
	if (!pecc_EC_POINT_mul(group, point, u1, pub_key, u2, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
		goto err;
	}
	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field)
	{
		if (!EC_POINT_get_affine_coordinates_GFp(group,
			point, X, NULL, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
			goto err;
		}
	}
	else /* NID_X9_62_characteristic_two_field */
	{
		/* SPEEDUP? */
		if (!pecc_EC_POINT_get_affine_coordinates_GF2m(group,
			point, X, NULL, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
			goto err;
		}
	}
	
	if (!BN_nnmod(u1, X, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
	/*  if the signature is correct u1 is equal to sig->r */
	ret = (BN_ucmp(u1, sig->r) == 0);
#endif


	{
		int _j,_i=0;
		int spu_ecdsa_result __attribute__((aligned(16))) = -10;
		spu_cmd_t cmd;
		spu_elem_t elem;
		spu_list_t arg_list;
		unsigned int m2[8] __attribute__((aligned(16)));
//		printf("BN_bn2hex(m) == %s\n",BN_bn2hex(m));
//		for(_i=0;_i<=m->top;++_i) {printf("(int*)m [%d]= 0x%016lx\n",_i,m->d[_i]);}
		memset(m2,0,sizeof(m2));
		memcpy(m2,m->d,m->top*8);
//		m2[6]=0;
		Qx     = BN_CTX_get(ctx);
		Qy     = BN_CTX_get(ctx);
		Qz     = BN_value_one();
		EC_POINT_get_affine_coordinates_GF2m(group,pub_key,Qx,Qy,ctx);
		elem.size=sizeof(arg_list);
		elem.data.all64=&arg_list;
		cmd.arg=elem;
//		printf("PPU: calling spu ecdsa_verify\n");
		cmd.type=3; //ecdsa_verify
		arg_list.size=6;
		_i=0;
		BN2spuarg(arg_list,Qx);
		BN2spuarg(arg_list,Qy);
		arg_list.list[_i].data.all64=m2;
		arg_list.list[_i++].size=32;
//		BN2spuarg(arg_list,m);
//		arg_list.list[_i-1].size=20;
		BN2spuarg(arg_list,sig->r);
		BN2spuarg(arg_list,sig->s);
		arg_list.list[_i].data.all64=&spu_ecdsa_result;
		arg_list.list[_i].size=sizeof(spu_ecdsa_result);
//		printf("GOT HERE OK! 0\n");
		_i = myspe_run(&pecc_stage4_spu_speid,NULL,(void*)&cmd);
//		printf("GOT HERE OK! 1\n");
//		printf("result: %d\n",spu_ecdsa_result);
		ret=spu_ecdsa_result;
	}
	
	//get e (variable "m" above)
	//get EC_POINT into Qx,Qy,Qz
	//get r from sig
	//get s from sig
err:
	BN_CTX_end(ctx);
	BN_CTX_free(ctx);
	if (point)
		EC_POINT_free(point);
	return ret;
}

static int orig_ecdsa_do_verify(const unsigned char *dgst, int dgst_len,
		const ECDSA_SIG *sig, EC_KEY *eckey)
{
	int ret = -1;
	BN_CTX   *ctx;
	BIGNUM   *order, *u1, *u2, *m, *X;
	EC_POINT *point = NULL;
	const EC_GROUP *group;
	const EC_POINT *pub_key;

	/* check input values */
	if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL ||
	    (pub_key = EC_KEY_get0_public_key(eckey)) == NULL || sig == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_MISSING_PARAMETERS);
		return -1;
	}

	ctx = BN_CTX_new();
	if (!ctx)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_MALLOC_FAILURE);
		return -1;
	}
	BN_CTX_start(ctx);
	order = BN_CTX_get(ctx);	
	u1    = BN_CTX_get(ctx);
	u2    = BN_CTX_get(ctx);
	m     = BN_CTX_get(ctx);
	X     = BN_CTX_get(ctx);
	if (!X)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
	
	if (!EC_GROUP_get_order(group, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
		goto err;
	}

	if (BN_is_zero(sig->r)          || BN_is_negative(sig->r) || 
	    BN_ucmp(sig->r, order) >= 0 || BN_is_zero(sig->s)  ||
	    BN_is_negative(sig->s)      || BN_ucmp(sig->s, order) >= 0)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_BAD_SIGNATURE);
		ret = 0;	/* signature is invalid */
		goto err;
	}
	/* calculate tmp1 = inv(S) mod order */
	if (!BN_mod_inverse(u2, sig->s, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
	/* digest -> m */
	if (!BN_bin2bn(dgst, dgst_len, m))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
	/* u1 = m * tmp mod order */
	if (!BN_mod_mul(u1, m, u2, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
	/* u2 = r * w mod q */
	if (!BN_mod_mul(u2, sig->r, u2, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}

	if ((point = EC_POINT_new(group)) == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_MALLOC_FAILURE);
		goto err;
	}
	if (!EC_POINT_mul(group, point, u1, pub_key, u2, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
		goto err;
	}
	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field)
	{
		if (!EC_POINT_get_affine_coordinates_GFp(group,
			point, X, NULL, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
			goto err;
		}
	}
	else /* NID_X9_62_characteristic_two_field */
	{
		if (!EC_POINT_get_affine_coordinates_GF2m(group,
			point, X, NULL, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_EC_LIB);
			goto err;
		}
	}
	
	if (!BN_nnmod(u1, X, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ERR_R_BN_LIB);
		goto err;
	}
	/*  if the signature is correct u1 is equal to sig->r */
	ret = (BN_ucmp(u1, sig->r) == 0);
err:
	BN_CTX_end(ctx);
	BN_CTX_free(ctx);
	if (point)
		EC_POINT_free(point);
	return ret;
}

static ECDSA_SIG *pecc_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, 
		const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *eckey)
{
	int     ok = 0;
	BIGNUM *kinv=NULL, *s, *m=NULL,*tmp=NULL,*order=NULL;
	const BIGNUM *ckinv;
	BN_CTX     *ctx = NULL;
	const EC_GROUP   *group;
	ECDSA_SIG  *ret;
	ECDSA_DATA *ecdsa;
	const BIGNUM *priv_key;

	ecdsa    = ecdsa_check(eckey);
	group    = EC_KEY_get0_group(eckey);
	priv_key = EC_KEY_get0_private_key(eckey);
	
	if (group == NULL || priv_key == NULL || ecdsa == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_PASSED_NULL_PARAMETER);
		return NULL;
	}

	ret = ECDSA_SIG_new();
	if (!ret)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
		return NULL;
	}
	s = ret->s;

	if ((ctx = BN_CTX_new()) == NULL || (order = BN_new()) == NULL ||
		(tmp = BN_new()) == NULL || (m = BN_new()) == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
		goto err;
	}

	if (!EC_GROUP_get_order(group, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_EC_LIB);
		goto err;
	}
	if (dgst_len > BN_num_bytes(order))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN,
			ECDSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
		goto err;
	}

	if (!BN_bin2bn(dgst, dgst_len, m))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
		goto err;
	}

	if((ckinv = BN_CTX_get(ctx)) == NULL || BN_rand_range(ckinv,order)!=1) {
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
		goto err;
	}
#if 0
		if (in_kinv == NULL || in_r == NULL)
		{
			if (!ECDSA_sign_setup(eckey, ctx, &kinv, &ret->r))
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN,ERR_R_ECDSA_LIB);
				goto err;
			}
			ckinv = kinv;
		}
		else
		{
			ckinv  = in_kinv;
			if (BN_copy(ret->r, in_r) == NULL)
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
				goto err;
			}
		}
#endif
	if(1){
		//spu_mod_mult_t mmarg;
		//mmarg.
		unsigned int m2[8] __attribute__((aligned(16)));
		spu_cmd_t cmd;
		spu_elem_t elem;
		int spu_ecdsa_result __attribute__((aligned(16))) = -10;
		int _i,_j,i;
		PECC_BN_t r,s;
		spu_list_t arg_list;
		ret->r = BN_dup(order);
		ret->s = BN_dup(order);
		_i=0;
		BN2spuarg(arg_list,priv_key);
		memset(m2,0,sizeof(m2));
		memcpy(m2,m->d,m->top*8);
		arg_list.list[_i].data.all64=m2;
		arg_list.list[_i++].size=32;
		//BN2spuarg(arg_list,m);
		BN2spuarg(arg_list,ckinv);
		BN2spuarg(arg_list,ret->r);
		BN2spuarg(arg_list,ret->s);
		arg_list.size=3;
		cmd.type=2; //ecdsa_sign
		elem.size=sizeof(arg_list);
		elem.data.all64=&arg_list;
		cmd.arg=elem;
//		printf("NO SECFAULT HERE!\n");
		i = myspe_run(&pecc_stage4_spu_speid,NULL,(void*)&cmd);
//		printf("result: %d\n",spu_ecdsa_result);
		ret->r->top=4;
		ret->s->top=4;
//		printf("from PPU: bn2hex r: %s\n",BN_bn2hex(ret->r));
//		printf("from PPU: bn2hex s: %s\n",BN_bn2hex(ret->s));
	}

#if 0
	do
	{
		if (in_kinv == NULL || in_r == NULL)
		{
			if (!ECDSA_sign_setup(eckey, ctx, &kinv, &ret->r))
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN,ERR_R_ECDSA_LIB);
				goto err;
			}
			ckinv = kinv;
		}
		else
		{
			ckinv  = in_kinv;
			if (BN_copy(ret->r, in_r) == NULL)
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
				goto err;
			}
		}

		if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
			goto err;
		}
		if (!BN_mod_add_quick(s, tmp, m, order))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
			goto err;
		}
		if (!BN_mod_mul(s, s, ckinv, order, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
			goto err;
		}
		if (BN_is_zero(s))
		{
			/* if kinv and r have been supplied by the caller
			 * don't to generate new kinv and r values */
			if (in_kinv != NULL && in_r != NULL)
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ECDSA_R_NEED_NEW_SETUP_VALUES);
				goto err;
			}
		}
		else
			/* s != 0 => we have a valid signature */
			break;
	}
	while (1);
#endif

	ok = 1;
err:
	if (!ok)
	{
		ECDSA_SIG_free(ret);
		ret = NULL;
	}
	if (ctx)
		BN_CTX_free(ctx);
	if (m)
		BN_clear_free(m);
	if (tmp)
		BN_clear_free(tmp);
	if (order)
		BN_free(order);
	if (kinv)
		BN_clear_free(kinv);
	return ret;
}

static ECDSA_SIG *orig_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, 
		const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *eckey)
{
	int     ok = 0;
	BIGNUM *kinv=NULL, *s, *m=NULL,*tmp=NULL,*order=NULL;
	const BIGNUM *ckinv;
	BN_CTX     *ctx = NULL;
	const EC_GROUP   *group;
	ECDSA_SIG  *ret;
	ECDSA_DATA *ecdsa;
	const BIGNUM *priv_key;

	ecdsa    = ecdsa_check(eckey);
	group    = EC_KEY_get0_group(eckey);
	priv_key = EC_KEY_get0_private_key(eckey);
	
	if (group == NULL || priv_key == NULL || ecdsa == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_PASSED_NULL_PARAMETER);
		return NULL;
	}

	ret = ECDSA_SIG_new();
	if (!ret)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
		return NULL;
	}
	s = ret->s;

	if ((ctx = BN_CTX_new()) == NULL || (order = BN_new()) == NULL ||
		(tmp = BN_new()) == NULL || (m = BN_new()) == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
		goto err;
	}

	if (!EC_GROUP_get_order(group, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_EC_LIB);
		goto err;
	}
	if (dgst_len > BN_num_bytes(order))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN,
			ECDSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
		goto err;
	}

	if (!BN_bin2bn(dgst, dgst_len, m))
	{
		ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
		goto err;
	}
	do
	{
		if (in_kinv == NULL || in_r == NULL)
		{
			if (!ECDSA_sign_setup(eckey, ctx, &kinv, &ret->r))
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN,ERR_R_ECDSA_LIB);
				goto err;
			}
			ckinv = kinv;
		}
		else
		{
			ckinv  = in_kinv;
			if (BN_copy(ret->r, in_r) == NULL)
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
				goto err;
			}
		}

		if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
			goto err;
		}
		if (!BN_mod_add_quick(s, tmp, m, order))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
			goto err;
		}
		if (!BN_mod_mul(s, s, ckinv, order, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
			goto err;
		}
		if (BN_is_zero(s))
		{
			/* if kinv and r have been supplied by the caller
			 * don't to generate new kinv and r values */
			if (in_kinv != NULL && in_r != NULL)
			{
				ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ECDSA_R_NEED_NEW_SETUP_VALUES);
				goto err;
			}
		}
		else
			/* s != 0 => we have a valid signature */
			break;
	}
	while (1);

	ok = 1;
err:
	if (!ok)
	{
		ECDSA_SIG_free(ret);
		ret = NULL;
	}
	if (ctx)
		BN_CTX_free(ctx);
	if (m)
		BN_clear_free(m);
	if (tmp)
		BN_clear_free(tmp);
	if (order)
		BN_free(order);
	if (kinv)
		BN_clear_free(kinv);
	return ret;
}
